{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "元编程就是关于创建操作源代码(比如修改、生成或包装原来的代码)的函数和类。 主要技术是使用装饰器、类装饰器和元类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.在函数上添加包装器\n",
    "\n",
    "你想在函数上添加一个包装器，增加额外的操作处理(比如日志、计时等)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "countdown 0.05076169967651367\n",
      "countdown 0.006003141403198242\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'countdown'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import time\n",
    "from functools import wraps\n",
    "\n",
    "def timethis(func):\n",
    "    @wraps(func)\n",
    "    def wrapper(*args, **kwargs):\n",
    "        start = time.time()\n",
    "        result = func(*args, **kwargs)\n",
    "        end = time.time()\n",
    "        print(func.__name__, end-start)\n",
    "        return result\n",
    "    return wrapper\n",
    "\n",
    "# test\n",
    "\n",
    "@timethis\n",
    "def countdown(n):\n",
    "    while n > 0:\n",
    "        n -= 1\n",
    "    \n",
    "countdown(1000000)\n",
    "countdown(100000)\n",
    "\n",
    "countdown.__name__\n",
    "countdown.__doc__\n",
    "countdown.__annotations__\n",
    "\n",
    "# @wraps 有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数。\n",
    "countdown.__wrapped__(100000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.创建装饰器时保留函数元信息\n",
    "\n",
    "你写了一个装饰器作用在某个函数上，但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。\n",
    "\n",
    "解决方案:\n",
    "\n",
    "任何时候你定义装饰器的时候，都应该使用 functools 库中的 @wraps 装饰器来注解底层包装函数。\n",
    "\n",
    "例如上面第1小节中的代码：`@wraps(func)`\n",
    "\n",
    "在编写装饰器的时候复制元信息是一个非常重要的部分。如果你忘记了使用 @wraps ， 那么你会发现被装饰函数丢失了所有有用的信息。\n",
    "\n",
    "比如如果忽略 @wraps 后的效果是下面这样的：\n",
    "\n",
    "```python\n",
    ">>> countdown.__name__\n",
    "'wrapper'\n",
    ">>> countdown.__doc__\n",
    ">>> countdown.__annotations__\n",
    "{}\n",
    ">>>\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.解除一个装饰器\n",
    "\n",
    "一个装饰器已经作用在一个函数上，你想撤销它，直接访问原始的未包装的那个函数。\n",
    "\n",
    "假设装饰器是通过 @wraps (参考2小节)来实现的，那么你可以通过访问 __wrapped__ 属性来访问原始函数：\n",
    "\n",
    "如果有多个包装器，那么访问 __wrapped__ 属性的行为是不可预知的，应该避免这样做。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.定义一个带参数的装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "spam\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Spam!\n"
     ]
    }
   ],
   "source": [
    "# 我们用一个例子详细阐述下接受参数的处理过程。 \n",
    "# 假设你想写一个装饰器，给函数添加日志功能，同时允许用户指定日志的级别和其他的选项。 \n",
    "\n",
    "from functools import wraps\n",
    "import logging\n",
    "\n",
    "def logged(level, name=None, message=None):\n",
    "    def decorate(func):\n",
    "        logname = name if name else func.__module__\n",
    "        log = logging.getLogger(logname)\n",
    "        logmsg = message if message else func.__name__\n",
    "\n",
    "        @wraps(func)\n",
    "        def wrapper(*args, **kwargs):\n",
    "            log.log(level, logmsg)\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapper\n",
    "    return decorate\n",
    "\n",
    "# test\n",
    "@logged(logging.DEBUG, name='112233')\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "@logged(logging.CRITICAL, name='example')\n",
    "def spam():\n",
    "    print('Spam!')\n",
    "\n",
    "\n",
    "add(1, 2)\n",
    "spam()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.可自定义属性的装饰器\n",
    "\n",
    "你想写一个装饰器来包装一个函数，并且允许用户提供参数在运行时控制装饰器行为。\n",
    "\n",
    "解决方案:\n",
    "\n",
    "引入一个访问函数，使用 nonlocal 来修改内部变量。 然后这个访问函数被作为一个属性赋值给包装函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "DEBUG:__main__:add\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "DEBUG:__main__:Add called\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:__main__:Add called\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from functools import wraps, partial\n",
    "import logging\n",
    "# Utility decorator to attach a function as an attribute of obj\n",
    "def attach_wrapper(obj, func=None):\n",
    "    if func is None:\n",
    "        return partial(attach_wrapper, obj)\n",
    "    setattr(obj, func.__name__, func)\n",
    "    return func\n",
    "\n",
    "def logged(level, name=None, message=None):\n",
    "    '''\n",
    "    Add logging to a function. level is the logging\n",
    "    level, name is the logger name, and message is the\n",
    "    log message. If name and message aren't specified,\n",
    "    they default to the function's module and name.\n",
    "    '''\n",
    "    def decorate(func):\n",
    "        logname = name if name else func.__module__\n",
    "        log = logging.getLogger(logname)\n",
    "        logmsg = message if message else func.__name__\n",
    "\n",
    "        @wraps(func)\n",
    "        def wrapper(*args, **kwargs):\n",
    "            log.log(level, logmsg)\n",
    "            return func(*args, **kwargs)\n",
    "\n",
    "        # Attach setter functions\n",
    "        @attach_wrapper(wrapper)\n",
    "        def set_level(newlevel):\n",
    "            nonlocal level\n",
    "            level = newlevel\n",
    "\n",
    "        @attach_wrapper(wrapper)\n",
    "        def set_message(newmsg):\n",
    "            nonlocal logmsg\n",
    "            logmsg = newmsg\n",
    "\n",
    "        return wrapper\n",
    "\n",
    "    return decorate\n",
    "\n",
    "# Example use\n",
    "@logged(logging.DEBUG)\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "@logged(logging.CRITICAL, 'example')\n",
    "def spam():\n",
    "    print('Spam!')\n",
    "\n",
    "\n",
    "logging.basicConfig(level=logging.DEBUG)\n",
    "add(2, 3)\n",
    "\n",
    "add.set_message('Add called')\n",
    "add(2, 3)\n",
    "\n",
    "add.set_level(logging.WARNING)\n",
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.带可选参数的装饰器\n",
    "\n",
    "你想写一个装饰器，既可以不传参数给它，比如 @decorator ， 也可以传递可选参数给它，比如 @decorator(x,y,z) 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "DEBUG:__main__:add\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "CRITICAL:example:spam\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Spam!\n"
     ]
    }
   ],
   "source": [
    "from functools import wraps, partial\n",
    "import logging\n",
    "\n",
    "def logged(func=None, *, level=logging.DEBUG, name=None, message=None):\n",
    "    if func is None:\n",
    "        return partial(logged, level=level, name=name, message=message)\n",
    "\n",
    "    logname = name if name else func.__module__\n",
    "    log = logging.getLogger(logname)\n",
    "    logmsg = message if message else func.__name__\n",
    "\n",
    "    @wraps(func)\n",
    "    def wrapper(*args, **kwargs):\n",
    "        log.log(level, logmsg)\n",
    "        return func(*args, **kwargs)\n",
    "\n",
    "    return wrapper\n",
    "\n",
    "# Example use\n",
    "@logged\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "@logged(level=logging.CRITICAL, name='example')\n",
    "def spam():\n",
    "    print('Spam!')\n",
    "\n",
    "\n",
    "add(1, 2)\n",
    "spam()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@logged\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "# 等价于\n",
    "\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "add = logged(add)\n",
    "\n",
    "#-------------\n",
    "\n",
    "@logged(level=logging.CRITICAL, name='example')\n",
    "def spam():\n",
    "    print('Spam!')\n",
    "\n",
    "# 等价于\n",
    "def spam():\n",
    "    print('Spam!')\n",
    "spam = logged(level=logging.CRITICAL, name='example')(spam)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.利用装饰器强制函数上的类型检查"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3\n",
      "1 hello 3\n"
     ]
    }
   ],
   "source": [
    "from inspect import signature\n",
    "from functools import wraps\n",
    "\n",
    "def typeassert(*ty_args, **ty_kwargs):\n",
    "    def decorate(func):\n",
    "        if not __debug__:\n",
    "            return func\n",
    "\n",
    "        sig = signature(func)\n",
    "        bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments\n",
    "\n",
    "        @wraps(func)\n",
    "        def wrapper(*args, **kwargs):\n",
    "            bound_values = sig.bind(*args, **kwargs)\n",
    "            for name, value in bound_values.arguments.items():\n",
    "                if name in bound_types:\n",
    "                    if not isinstance(value, bound_types[name]):\n",
    "                        raise TypeError('Argument {} must be {}'.format(name, bound_types[name]))\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapper\n",
    "    return decorate\n",
    "\n",
    "# test\n",
    "@typeassert(int, z=int)\n",
    "def spam(x, y, z=42):\n",
    "    print(x, y, z)\n",
    "\n",
    "spam(1, 2, 3)\n",
    "\n",
    "spam(1, \"hello\", 3)\n",
    "\n",
    "# spam(1, 2, \"hello\")\n",
    "# Argument z must be <class 'int'>\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Signature (x, y, z=42)>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "mappingproxy({'x': <Parameter \"x\">,\n",
       "              'y': <Parameter \"y\">,\n",
       "              'z': <Parameter \"z=42\">})"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-------------------- sig.parameters-x\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Parameter \"x\">"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'x'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "inspect._empty"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-------------------- sig.parameters-z\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'z'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-------------------- bound_types\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<BoundArguments (x=<class 'int'>, z=<class 'int'>)>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'x': int, 'z': int}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-------------------- bound_values\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<BoundArguments (x=1, y=2, z=3)>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'x': 1, 'y': 2, 'z': 3}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test\n",
    "from inspect import signature\n",
    "def spam2(x, y, z=42):\n",
    "    pass\n",
    "\n",
    "sig = signature(spam2)\n",
    "sig\n",
    "sig.parameters\n",
    "\n",
    "print('-'*20, 'sig.parameters-x')\n",
    "sig.parameters['x']\n",
    "sig.parameters['x'].name\n",
    "sig.parameters['x'].default\n",
    "sig.parameters['x'].kind\n",
    "\n",
    "print('-'*20, 'sig.parameters-z')\n",
    "sig.parameters['z'].name\n",
    "sig.parameters['z'].default\n",
    "sig.parameters['z'].kind\n",
    "\n",
    "# 执行从指定类型到名称的部分绑定\n",
    "print('-'*20, 'bound_types')\n",
    "bound_types = sig.bind_partial(int,z=int)\n",
    "bound_types\n",
    "bound_types.arguments\n",
    "\n",
    "print('-'*20, 'bound_values')\n",
    "bound_values = sig.bind(1, 2, 3)\n",
    "bound_values\n",
    "bound_values.arguments\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.将装饰器定义为类的一部分\n",
    "\n",
    "你想在类中定义装饰器，并将其作用在其他函数或方法上。\n",
    "\n",
    "在类里面定义装饰器很简单，但是你首先要确认它的使用方式。比如到底是作为一个实例方法还是类方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-------------------- spam1\n",
      "Decorator1\n",
      "spam1\n",
      "-------------------- spam2\n",
      "Decorator2\n",
      "spam2\n"
     ]
    }
   ],
   "source": [
    "from functools import wraps\n",
    "\n",
    "from sympy import fu\n",
    "\n",
    "class A:\n",
    "    def decorator1(self, func):\n",
    "        @wraps(func)\n",
    "        def wrapper(*args, **kwargs):\n",
    "            print('Decorator1')\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapper\n",
    "    \n",
    "    @classmethod\n",
    "    def decorator2(cls, func):\n",
    "        @wraps(func)\n",
    "        def wrapper(*args, **kwargs):\n",
    "            print('Decorator2')\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapper\n",
    "    \n",
    "\n",
    "# use test\n",
    "a = A()\n",
    "\n",
    "@a.decorator1\n",
    "def spam1():\n",
    "    print('spam1')\n",
    "\n",
    "@A.decorator2\n",
    "def spam2():\n",
    "    print('spam2')\n",
    "\n",
    "print('-'*20, 'spam1')\n",
    "spam1()\n",
    "print('-'*20, 'spam2')\n",
    "spam2()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9.将装饰器定义为类\n",
    "\n",
    "为了将装饰器定义成一个类，你需要确保它实现了 __call__() 和 __get__() 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Spam object at 0x7fd0280ddb50> 1\n",
      "<__main__.Spam object at 0x7fd0280ddb50> 2\n",
      "<__main__.Spam object at 0x7fd0280ddb50> 3\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import types\n",
    "from functools import wraps\n",
    "\n",
    "class Profiled:\n",
    "    def __init__(self, func):\n",
    "        wraps(func)(self)\n",
    "        self.ncalls = 0\n",
    "\n",
    "    # 这个call方法，作用于下面的add方法\n",
    "    def __call__(self, *args, **kwargs):\n",
    "        self.ncalls += 1\n",
    "        return self.__wrapped__(*args, **kwargs)\n",
    "\n",
    "    # 这个get方法，作用于下面的Spam.bar\n",
    "    # Profiled 实例会调用get方法来寻找bar方法\n",
    "    def __get__(self, instance, cls):\n",
    "        if instance is None:\n",
    "            return self\n",
    "        else:\n",
    "            return types.MethodType(self, instance)\n",
    "\n",
    "\n",
    "@Profiled\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "\n",
    "add(4, 5)\n",
    "add.ncalls\n",
    "\n",
    "\n",
    "class Spam:\n",
    "    @Profiled\n",
    "    def bar(self, x):\n",
    "        print(self, x)\n",
    "\n",
    "s = Spam()\n",
    "s.bar(1)\n",
    "s.bar(2)\n",
    "s.bar(3)\n",
    "Spam.bar.ncalls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.为类和静态方法提供装饰器\n",
    "\n",
    "给类或静态方法提供装饰器是很简单的，不过要确保装饰器在 @classmethod 或 @staticmethod 之前。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Spam object at 0x7fd02807cf70> 1000000\n",
      "0.13767695426940918\n",
      "<class '__main__.Spam'> 1000000\n",
      "0.13437318801879883\n",
      "1000000\n",
      "0.13281869888305664\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "from functools import wraps\n",
    "\n",
    "def timethis(func):\n",
    "    @wraps(func)\n",
    "    def wrapper(*args, **kwargs):\n",
    "        start = time.time()\n",
    "        r = func(*args, **kwargs)\n",
    "        end = time.time()\n",
    "        print(end-start)\n",
    "        return r\n",
    "    return wrapper\n",
    "\n",
    "\n",
    "class Spam:\n",
    "    @timethis\n",
    "    def instance_method(self, n):\n",
    "        print(self, n)\n",
    "        while n > 0:\n",
    "            n -= 1\n",
    "\n",
    "    @classmethod\n",
    "    @timethis\n",
    "    def class_method(cls, n):\n",
    "        print(cls, n)\n",
    "        while n > 0:\n",
    "            n -= 1\n",
    "\n",
    "    @staticmethod\n",
    "    @timethis\n",
    "    def static_method(n):\n",
    "        print(n)\n",
    "        while n > 0:\n",
    "            n -= 1\n",
    "\n",
    "\n",
    "# test\n",
    "s = Spam()\n",
    "s.instance_method(1000000)\n",
    "Spam.class_method(1000000)\n",
    "Spam.static_method(1000000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.装饰器为被包装函数增加参数\n",
    "\n",
    "你想在装饰器中给被包装函数增加额外的参数，但是不能影响这个函数现有的调用规则。\n",
    "\n",
    "可以使用关键字参数来给被包装函数增加额外参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3\n",
      "Calling spam\n",
      "1 2 3\n",
      "(a, b, c, *, debug=False)\n"
     ]
    }
   ],
   "source": [
    "from functools import wraps\n",
    "import inspect\n",
    "\n",
    "def optional_debug(func):\n",
    "    if 'debug' in inspect.getfullargspec(func).args:\n",
    "        raise TypeError('debug argument already defined')\n",
    "\n",
    "    @wraps(func)\n",
    "    def wrapper(*args, debug=False, **kwargs):\n",
    "        if debug:\n",
    "            print('Calling', func.__name__)\n",
    "        return func(*args, **kwargs)\n",
    "\n",
    "    # fix 被包装函数的函数签名\n",
    "    sig = inspect.signature(func)\n",
    "    parms = list(sig.parameters.values())\n",
    "    parms.append(inspect.Parameter('debug',\n",
    "                inspect.Parameter.KEYWORD_ONLY,\n",
    "                default=False))\n",
    "    wrapper.__signature__ = sig.replace(parameters=parms) \n",
    "    return wrapper\n",
    "\n",
    "@optional_debug\n",
    "def spam(a,b,c):\n",
    "    print(a,b,c)\n",
    "\n",
    "spam(1,2,3)\n",
    "spam(1,2,3, debug=True)\n",
    "\n",
    "print(inspect.signature(spam))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.使用装饰器扩充类的功能\n",
    "\n",
    "你想通过反省或者重写类定义的某部分来修改它的行为，但是你又不希望使用继承或元类的方式。\n",
    "\n",
    "这种情况可能是类装饰器最好的使用场景了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "getting: x\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "getting: spam\n"
     ]
    }
   ],
   "source": [
    "def log_getattribute(cls):\n",
    "    # Get the original implementation\n",
    "    orig_getattribute = cls.__getattribute__\n",
    "\n",
    "    # Make a new definition\n",
    "    def new_getattribute(self, name):\n",
    "        print('getting:', name)\n",
    "        return orig_getattribute(self, name)\n",
    "\n",
    "    # Attach to the class and return\n",
    "    cls.__getattribute__ = new_getattribute\n",
    "    return cls\n",
    "\n",
    "# Example use\n",
    "@log_getattribute\n",
    "class A:\n",
    "    def __init__(self,x):\n",
    "        self.x = x\n",
    "    def spam(self):\n",
    "        pass\n",
    "\n",
    "\n",
    "# test\n",
    "a = A(42)\n",
    "a.x\n",
    "a.spam()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 13.使用元类控制实例的创建\n",
    "\n",
    "你想通过改变实例创建方式来实现单例、缓存或其他类似的特性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Creating Spam\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 单例模式\n",
    "class Singleton(type):\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        self.__instance = None\n",
    "        super().__init__(*args, **kwargs)\n",
    "\n",
    "    def __call__(self, *args, **kwargs):\n",
    "        if self.__instance is None:\n",
    "            self.__instance = super().__call__(*args, **kwargs)\n",
    "            return self.__instance\n",
    "        else:\n",
    "            return self.__instance\n",
    "\n",
    "# Example\n",
    "class Spam(metaclass=Singleton):\n",
    "    def __init__(self):\n",
    "        print('Creating Spam')\n",
    "\n",
    "\n",
    "# test\n",
    "a = Spam()\n",
    "b = Spam()\n",
    "a is b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 14.捕获类的属性定义顺序\n",
    "\n",
    "你想自动记录一个类中属性和方法定义的顺序， 然后可以利用它来做很多操作（比如序列化、映射到数据库等等）。\n",
    "\n",
    "利用元类可以很容易的捕获类的定义信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedMeta.__prepare__ <class '__main__.OrderedMeta'> Structure ()\n",
      "OrderedMeta.__new__ <class '__main__.OrderedMeta'> Structure () OrderedDict([('__module__', '__main__'), ('__qualname__', 'Structure'), ('as_csv', <function Structure.as_csv at 0x7f0e2c53b700>)])\n",
      "OrderedMeta.__prepare__ <class '__main__.OrderedMeta'> Stock (<class '__main__.Structure'>,)\n",
      "OrderedMeta.__new__ <class '__main__.OrderedMeta'> Stock (<class '__main__.Structure'>,) OrderedDict([('__module__', '__main__'), ('__qualname__', 'Stock'), ('name', <__main__.String object at 0x7f0e2c4b84f0>), ('shares', <__main__.Integer object at 0x7f0e2c4b8520>), ('price', <__main__.Float object at 0x7f0e2c4b8130>), ('__init__', <function Stock.__init__ at 0x7f0e2c53bf70>)])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<__main__.Stock at 0x7f0e2c557430>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import OrderedDict\n",
    "\n",
    "# A set of descriptors for various types\n",
    "class Typed:\n",
    "    _expected_type = type(None)\n",
    "    def __init__(self, name=None):\n",
    "        self._name = name\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if not isinstance(value, self._expected_type):\n",
    "            raise TypeError('Expected ' + str(self._expected_type))\n",
    "        instance.__dict__[self._name] = value\n",
    "\n",
    "class Integer(Typed):\n",
    "    _expected_type = int\n",
    "\n",
    "class Float(Typed):\n",
    "    _expected_type = float\n",
    "\n",
    "class String(Typed):\n",
    "    _expected_type = str\n",
    "\n",
    "# Metaclass that uses an OrderedDict for class body\n",
    "class OrderedMeta(type):\n",
    "    def __new__(cls, clsname, bases, clsdict):\n",
    "        print('OrderedMeta.__new__', cls, clsname, bases, clsdict)\n",
    "        d = dict(clsdict)\n",
    "        order = []\n",
    "        for name, value in clsdict.items():\n",
    "            if isinstance(value, Typed):\n",
    "                value._name = name\n",
    "                order.append(name)\n",
    "        d['_order'] = order\n",
    "        return type.__new__(cls, clsname, bases, d)\n",
    "\n",
    "    # 这个方法会在开始定义类和它的父类的时候被执行。\n",
    "    # 它必须返回一个映射对象以便在类定义体中被使用到。\n",
    "    @classmethod\n",
    "    def __prepare__(cls, clsname, bases):\n",
    "        print('OrderedMeta.__prepare__', cls, clsname, bases)\n",
    "        return OrderedDict()\n",
    "\n",
    "\n",
    "class Structure(metaclass=OrderedMeta):\n",
    "    def as_csv(self):\n",
    "        return ','.join(str(getattr(self,name)) for name in self._order)\n",
    "\n",
    "# Example use\n",
    "class Stock(Structure):\n",
    "    name = String()\n",
    "    shares = Integer()\n",
    "    price = Float()\n",
    "\n",
    "    def __init__(self, name, shares, price):\n",
    "        self.name = name\n",
    "        self.shares = shares\n",
    "        self.price = price\n",
    "\n",
    "\n",
    "# test\n",
    "s = Stock('GOOG',100,490.1)\n",
    "s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 15.定义有可选参数的元类\n",
    "\n",
    "在定义类的时候，Python允许我们使用 ``metaclass``关键字参数来指定特定的元类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为了使元类支持这些关键字参数，你必须确保在 __prepare__() , __new__() 和 __init__() 方法中 都使用强制关键字参数。\n",
    "class MyMeta(type):\n",
    "    # Optional\n",
    "    @classmethod\n",
    "    def __prepare__(cls, name, bases, *, debug=False, synchronize=False):\n",
    "        # Custom processing\n",
    "        pass\n",
    "        return super().__prepare__(name, bases)\n",
    "\n",
    "    # Required\n",
    "    def __new__(cls, name, bases, ns, *, debug=False, synchronize=False):\n",
    "        # Custom processing\n",
    "        pass\n",
    "        return super().__new__(cls, name, bases, ns)\n",
    "\n",
    "    # Required\n",
    "    def __init__(self, name, bases, ns, *, debug=False, synchronize=False):\n",
    "        # Custom processing\n",
    "        pass\n",
    "        super().__init__(name, bases, ns)\n",
    "\n",
    "# 然后，就可以在自定义元类中我们还可以提供这些关键字参数\n",
    "class Spam(metaclass=MyMeta, debug=True, synchronize=True):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "给一个元类添加可选关键字参数需要你完全弄懂类创建的所有步骤， 因为这些参数会被传递给每一个相关的方法。 \n",
    "__prepare__() 方法在所有类定义开始执行前首先被调用，用来创建类命名空间。 通常来讲，这个方法只是简单的返回一个字典或其他映射对象。 \n",
    "__new__() 方法被用来实例化最终的类对象。它在类的主体被执行完后开始执行。 \n",
    "__init__() 方法最后被调用，用来执行其他的一些初始化工作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 16.*args和**kwargs的强制参数签名\n",
    "\n",
    "你有一个函数或方法，它使用*args和**kwargs作为参数，这样使得它比较通用， 但有时候你想检查传递进来的参数是不是某个你想要的类型。\n",
    "\n",
    "对任何涉及到操作函数调用签名的问题，你都应该使用 inspect 模块中的签名特性。 \n",
    "\n",
    "我们最主要关注两个类：Signature 和 Parameter 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Signature (x, y=42, *, z=None)>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x 1\n",
      "y 2\n",
      "z 3\n",
      "--------------------\n",
      "x 1\n",
      "z 4\n"
     ]
    }
   ],
   "source": [
    "from inspect import Signature, Parameter\n",
    "# Make a signature for a func(x, y=42, *, z=None)\n",
    "parms = [ Parameter('x', Parameter.POSITIONAL_OR_KEYWORD),\n",
    "        Parameter('y', Parameter.POSITIONAL_OR_KEYWORD, default=42),\n",
    "        Parameter('z', Parameter.KEYWORD_ONLY, default=None) ]\n",
    "\n",
    "sig = Signature(parms)\n",
    "sig\n",
    "\n",
    "# 一旦你有了一个签名对象，你就可以使用它的 bind() 方法很容易的将它绑定到 *args 和 **kwargs 上去\n",
    "def func(*args, **kwargs):\n",
    "    bound_values = sig.bind(*args, **kwargs)\n",
    "    for name, value in bound_values.arguments.items():\n",
    "        print(name,value)\n",
    "\n",
    "func(1, 2, z=3)\n",
    "\n",
    "print('-'*20)\n",
    "func(1, z=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(name, shares, price)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<__main__.Stock at 0x7f0e1c14ecd0>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from inspect import Signature, Parameter\n",
    "import inspect\n",
    "\n",
    "def make_sig(*names):\n",
    "    parms = [Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)\n",
    "            for name in names]\n",
    "    return Signature(parms)\n",
    "\n",
    "class Structure:\n",
    "    __signature__ = make_sig()\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        bound_values = self.__signature__.bind(*args, **kwargs)\n",
    "        for name, value in bound_values.arguments.items():\n",
    "            setattr(self, name, value)\n",
    "\n",
    "# Example use\n",
    "class Stock(Structure):\n",
    "    __signature__ = make_sig('name', 'shares', 'price')\n",
    "\n",
    "class Point(Structure):\n",
    "    __signature__ = make_sig('x', 'y')\n",
    "\n",
    "\n",
    "# test\n",
    "print(inspect.signature(Stock))\n",
    "s1 = Stock('ACME', 100, 490.1)\n",
    "s1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 17.在类上强制使用编程规约\n",
    "\n",
    "你的程序包含一个很大的类继承体系，你希望强制执行某些编程规约（或者代码诊断）来帮助程序员保持清醒。\n",
    "\n",
    "如果你想监控类的定义，通常可以通过定义一个元类。一个基本元类通常是继承自 type 并重定义它的 __new__() 方法 或者是 __init__() 方法。\n",
    "\n",
    "```python\n",
    "\n",
    "class MyMeta(type):\n",
    "    def __new__(self, clsname, bases, clsdict):\n",
    "        # clsname is name of class being defined\n",
    "        # bases is tuple of base classes\n",
    "        # clsdict is class dictionary\n",
    "        return super().__new__(cls, clsname, bases, clsdict)\n",
    "\n",
    "# 或者\n",
    "class MyMeta(type):\n",
    "    def __init__(self, clsname, bases, clsdict):\n",
    "        super().__init__(clsname, bases, clsdict)\n",
    "        # clsname is name of class being defined\n",
    "        # bases is tuple of base classes\n",
    "        # clsdict is class dictionary\n",
    "\n",
    "# 使用这个元类\n",
    "class Root(metaclass=MyMeta):\n",
    "    pass\n",
    "\n",
    "class A(Root):\n",
    "    pass\n",
    "\n",
    "class B(Root):\n",
    "    pass\n",
    "\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 18.以编程方式定义类\n",
    "\n",
    "你可以使用函数 types.new_class() 来初始化新的类对象。 你需要做的只是提供类的名字、父类元组、关键字参数，以及一个用成员变量填充类字典的回调函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4555.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import types\n",
    "\n",
    "def __init__(self, name, shares, price):\n",
    "    self.name = name\n",
    "    self.shares = shares\n",
    "    self.price = price\n",
    "\n",
    "def cost(self):\n",
    "    return self.shares * self.price\n",
    "\n",
    "cls_dict = {\n",
    "    '__init__' : __init__,\n",
    "    'cost' : cost,\n",
    "}\n",
    "\n",
    "Stock = types.new_class('Stock', (), {},  lambda ns: ns.update(cls_dict))\n",
    "#  每次当一个类被定义后，它的 __module__ 属性包含定义它的模块名\n",
    "Stock.__module__ = __name__\n",
    "\n",
    "s = Stock('ACME', 50, 91.1)\n",
    "s.cost()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 19.在定义的时候初始化类的成员\n",
    "\n",
    "你想在类被定义的时候就初始化一部分类的成员，而不是要等到实例被创建后。\n",
    "\n",
    "有些烧脑...没看懂。\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p19_initializing_class_members_at_definition_time.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 20.利用函数注解实现方法重载\n",
    "\n",
    "还是烧脑...\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p20_implement_multiple_dispatch_with_function_annotations.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 21.避免重复的属性方法\n",
    "\n",
    "你在类中需要重复的定义一些执行相同逻辑的属性方法，比如进行类型检查，怎样去简化这些重复代码呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, name ,age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    @property\n",
    "    def name(self):\n",
    "        return self._name\n",
    "\n",
    "    @name.setter\n",
    "    def name(self, value):\n",
    "        if not isinstance(value, str):\n",
    "            raise TypeError('name must be a string')\n",
    "        self._name = value\n",
    "\n",
    "    @property\n",
    "    def age(self):\n",
    "        return self._age\n",
    "\n",
    "    @age.setter\n",
    "    def age(self, value):\n",
    "        if not isinstance(value, int):\n",
    "            raise TypeError('age must be an int')\n",
    "        self._age = value\n",
    "\n",
    "# ------------------------- 改进如下\n",
    "\n",
    "def typed_property(name, expected_type):\n",
    "    storage_name = '_' + name\n",
    "\n",
    "    @property\n",
    "    def prop(self):\n",
    "        return getattr(self, storage_name)\n",
    "\n",
    "    @prop.setter\n",
    "    def prop(self, value):\n",
    "        if not isinstance(value, expected_type):\n",
    "            raise TypeError('{} must be a {}'.format(name, expected_type))\n",
    "        setattr(self, storage_name, value)\n",
    "\n",
    "    return prop\n",
    "\n",
    "# Example use\n",
    "class Person:\n",
    "    name = typed_property('name', str)\n",
    "    age = typed_property('age', int)\n",
    "\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 22.定义上下文管理器的简单方法\n",
    "\n",
    "你想自己去实现一个新的上下文管理器，以便使用with语句。\n",
    "\n",
    "通常情况下，如果要写一个上下文管理器，你需要定义一个类，里面包含一个 __enter__() 和一个 __exit__() 方法。\n",
    "\n",
    "实现一个新的上下文管理器的最简单的方法就是使用 contextlib 模块中的 @contextmanager 装饰器。\n",
    "\n",
    "@contextmanager 应该仅仅用来写自包含的上下文管理函数。 \n",
    "\n",
    "如果你有一些对象(比如一个文件、网络连接或锁)，需要支持 with 语句，那么你就需要单独实现 __enter__() 方法和 __exit__() 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "class timethis:\n",
    "    def __init__(self, label):\n",
    "        self.label = label\n",
    "\n",
    "    def __enter__(self):\n",
    "        self.start = time.time()\n",
    "\n",
    "    def __exit__(self, exc_ty, exc_val, exc_tb):\n",
    "        end = time.time()\n",
    "        print('{}: {}'.format(self.label, end - self.start))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "counting: 0.8323073387145996\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "from contextlib import contextmanager\n",
    "\n",
    "# 在函数 timethis() 中，\n",
    "# yield 之前的代码会在上下文管理器中作为 __enter__() 方法执行， \n",
    "# 所有在 yield 之后的代码会作为 __exit__() 方法执行。 \n",
    "# 如果出现了异常，异常会在yield语句那里抛出。\n",
    "@contextmanager\n",
    "def timethis(label):\n",
    "    start = time.time()\n",
    "    try:\n",
    "        yield\n",
    "    finally:\n",
    "        end = time.time()\n",
    "        print('{}: {}'.format(label, end - start))\n",
    "\n",
    "# test\n",
    "with timethis('counting'):\n",
    "    n = 10000000\n",
    "    while n > 0:\n",
    "        n -= 1\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 23.在局部变量域中执行代码\n",
    "\n",
    "你想在使用范围内执行某个代码片段，并且希望在执行后所有的结果都不可见。\n",
    "\n",
    "使用 `exec()`方法，但不推荐这么做。\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p23_executing_code_with_local_side_effects.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 24.解析与分析Python源码\n",
    "\n",
    "此小节过于高级，用了到抽象语法树（AST）的知识。\n",
    "\n",
    "跳过。\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p24_parse_and_analyzing_python_source.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 25.拆解Python字节码\n",
    "\n",
    "你想通过将你的代码反编译成低级的字节码来查看它底层的工作机制。\n",
    "\n",
    "dis 模块可以被用来输出任何Python函数的反编译结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'|\\x00d\\x01k\\x04r\\x1ct\\x00d\\x02|\\x00\\x83\\x02\\x01\\x00|\\x00d\\x038\\x00}\\x00q\\x00t\\x00d\\x04\\x83\\x01\\x01\\x00d\\x00S\\x00'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  2     >>    0 LOAD_FAST                0 (n)\n",
      "              2 LOAD_CONST               1 (0)\n",
      "              4 COMPARE_OP               4 (>)\n",
      "              6 POP_JUMP_IF_FALSE       28\n",
      "\n",
      "  3           8 LOAD_GLOBAL              0 (print)\n",
      "             10 LOAD_CONST               2 ('T-minus')\n",
      "             12 LOAD_FAST                0 (n)\n",
      "             14 CALL_FUNCTION            2\n",
      "             16 POP_TOP\n",
      "\n",
      "  4          18 LOAD_FAST                0 (n)\n",
      "             20 LOAD_CONST               3 (1)\n",
      "             22 INPLACE_SUBTRACT\n",
      "             24 STORE_FAST               0 (n)\n",
      "             26 JUMP_ABSOLUTE            0\n",
      "\n",
      "  5     >>   28 LOAD_GLOBAL              0 (print)\n",
      "             30 LOAD_CONST               4 ('Blastoff!')\n",
      "             32 CALL_FUNCTION            1\n",
      "             34 POP_TOP\n",
      "             36 LOAD_CONST               0 (None)\n",
      "             38 RETURN_VALUE\n"
     ]
    }
   ],
   "source": [
    "def countdown(n):\n",
    "    while n > 0:\n",
    "        print('T-minus', n)\n",
    "        n -= 1\n",
    "    print('Blastoff!')\n",
    "\n",
    "countdown.__code__.co_code\n",
    "\n",
    "import dis\n",
    "dis.dis(countdown)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "caf1c2fcf97217de91eafa76b907d50f9ea378f5ffbee7f571142d119bb6a771"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
